package com.sunday.common.mybatis.plus.query;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunday.common.core.lambda.LambdaOperation;
import com.sunday.common.core.lambda.toolkit.support.SFunction;
import com.sunday.common.core.remoting.base.page.PageDTO;
import com.sunday.common.core.remoting.base.page.PageResultDTO;
import com.sunday.common.mybatis.plus.core.MybatisPlusFieldMapping;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.lang.Nullable;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

/**
 * 分页 Wrapper
 *
 * @author zsy
 * @since 2022/10/27
 */
@Slf4j
public class PageWrapper {

    public static <T> Page<T> map(PageDTO<?> pageDto, SFunction<T, ?> column) {
        return map(pageDto, column, true);
    }

    /**
     * 根据 PageInfoDTO 转化为 Mybatis plus 组件所需的 Page 对象, 进行数据库查询
     * com.sunday.common.core.remoting.base.page.PageInfoDTO -> com.baomidou.mybatisplus.extension.plugins.pagination.Page
     *
     * @param pageDto 分页对象
     * @param column  缺省排序字段
     * @param asc     缺省排序顺序
     * @return {@link Page<T>}
     */
    public static <T> Page<T> map(PageDTO<?> pageDto, SFunction<T, ?> column, boolean asc) {
        assert column != null : "column must not be null";
        String columnName = MybatisPlusFieldMapping.getColumnName(column);
        Class targetClass = LambdaOperation.getInstantiatedClass(column);
        return map(pageDto, targetClass, List.of(asc ? OrderItem.asc(columnName) : OrderItem.desc(columnName)));
    }

    /**
     * 根据 PageInfoDTO 转化为 Mybatis plus 组件所需的 Page 对象, 进行数据库查询
     * com.sunday.common.core.remoting.base.page.PageInfoDTO -> com.baomidou.mybatisplus.extension.plugins.pagination.Page
     *
     * @param pageDto           分页对象
     * @param orderItemWrappers 缺省排序
     * @return {@link Page<T>}
     */
    public static <T> Page<T> map(PageDTO<?> pageDto, OrderItemWrapper<T>... orderItemWrappers) {
        assert ObjectUtils.isNotEmpty(orderItemWrappers) : "orderItemWrappers must not be empty";
        List<OrderItem> defaultOrder = Arrays.stream(orderItemWrappers)
                .map(wrapper -> wrapper.isAsc() ?
                        OrderItem.asc(MybatisPlusFieldMapping.getColumnName(wrapper.getColumn())) :
                        OrderItem.desc(MybatisPlusFieldMapping.getColumnName(wrapper.getColumn())))
                .toList();
        Class targetClass = LambdaOperation.getInstantiatedClass(orderItemWrappers[0].getColumn());
        return map(pageDto, targetClass, defaultOrder);
    }

    /**
     * 根据 PageInfoDTO 转化为 Mybatis plus 组件所需的 Page 对象, 进行数据库查询
     * com.sunday.common.core.remoting.base.page.PageInfoDTO -> com.baomidou.mybatisplus.extension.plugins.pagination.Page
     *
     * @param pageDto      分页对象
     * @param targetClass  分页目标对象
     * @param defaultOrder 缺省排序
     * @return {@link Page<T>}
     */
    public static <T> Page<T> map(PageDTO<?> pageDto, Class<T> targetClass, List<OrderItem> defaultOrder) {
        assert pageDto != null : "pageDto must not be null";
        assert targetClass != null : "targetClass must not be null";
        assert defaultOrder != null : "defaultOrder must not be null";
        Page page = Page.of(pageDto.getCurrent(), pageDto.getPageSize());
        page.setOrders(
                Optional.ofNullable(pageDto.getOrders())
                        .filter(ObjectUtils::isNotEmpty)
                        .map(orders -> orders.stream()
                                .map(order -> order.isAsc()
                                        ? OrderItem.asc(MybatisPlusFieldMapping.getColumnName(targetClass, order.getColumn()))
                                        : OrderItem.desc(MybatisPlusFieldMapping.getColumnName(targetClass, order.getColumn()))
                                )
                                .toList()
                        )
                        .orElse(defaultOrder));
        return page;
    }

    /**
     * 根据数据库检索返回 IPage 转化为 PageResultDTO，进行查询结果返回
     * com.baomidou.mybatisplus.core.metadata.IPage  -> com.sunday.common.core.remoting.base.page.PageResultDTO
     *
     * @param page
     * @param values
     * @return {@link PageResultDTO<T>}
     */
    public static <T> PageResultDTO<T> result(IPage<?> page, @Nullable List<T> values) {
        assert page != null : "page must not be null";
        PageResultDTO<T> pageResultDto = PageResultDTO.of(page.getCurrent(), page.getSize(), page.getTotal());
        pageResultDto.setPages(page.getPages());
        pageResultDto.setRecords(values);
        return pageResultDto;
    }

    /**
     * 根据数据库检索返回 IPage 并通过 Function 转化为 PageResultDTO，进行查询结果返回
     *
     * @param page
     * @param converter
     * @return {@link PageResultDTO<R>}
     */
    public static <T, R> PageResultDTO<R> result(IPage<T> page, Function<T, R> converter) {
        assert page != null : "page must not be null";
        assert converter != null : "converter must not be null";
        PageResultDTO<R> pageResultDto = PageResultDTO.of(page.getCurrent(), page.getSize(), page.getTotal());
        pageResultDto.setPages(page.getPages());
        pageResultDto.setRecords(
                page.getRecords()
                        .stream()
                        .map(converter::apply)
                        .toList()
        );
        return pageResultDto;
    }

    /**
     * PageResultDTO 缺省结果返回
     *
     * @return {@link PageResultDTO<T>}
     */
    public static <T> PageResultDTO<T> empty() {
        return new PageResultDTO<>();
    }

}
